{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "46ebd3f9",
   "metadata": {},
   "source": [
    "# 普通继承"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 148,
   "id": "d5affe61",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 1. 普通父类（非抽象类，可实例化）\n",
    "class Person:\n",
    "    def __init__(self, name):\n",
    "        self.name = name  # 公共属性：姓名\n",
    "    def eat(self):\n",
    "        \"\"\"父类的吃饭方法（有具体实现）\"\"\"\n",
    "        print(f\"{self.name}在吃饭（父类默认实现）\")\n",
    "    def work(self):\n",
    "        \"\"\"父类的工作方法（有具体实现）\"\"\"\n",
    "        print(f\"{self.name}在工作（父类默认实现）\")\n",
    "# 2. 子类Student继承Person\n",
    "class Student(Person):\n",
    "    # 重写父类的work方法（子类特有实现）\n",
    "    def work(self):\n",
    "        print(f\"{self.name}在写作业（学生的工作）\")\n",
    "    # 新增子类特有方法\n",
    "    def study(self):\n",
    "        print(f\"{self.name}在学习\")\n",
    "# 3. 子类Teacher继承Person\n",
    "class Teacher(Person):\n",
    "    # 重写父类的work方法（子类特有实现）\n",
    "    def work(self):\n",
    "        print(f\"{self.name}在讲课（老师的工作）\")\n",
    "    # 不重写eat方法，直接继承父类实现"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 149,
   "id": "5f5f0c25",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "普通人在吃饭（父类默认实现）\n",
      "普通人在工作（父类默认实现）\n"
     ]
    }
   ],
   "source": [
    "# 实例化父类（普通类可直接实例化）\n",
    "person = Person(\"普通人\")\n",
    "person.eat()  # 调用父类方法\n",
    "person.work()  # 调用父类方法"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 150,
   "id": "83543edd",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "小明在吃饭（父类默认实现）\n",
      "小明在写作业（学生的工作）\n",
      "小明在学习\n"
     ]
    }
   ],
   "source": [
    "# 实例化Student子类\n",
    "student = Student(\"小明\")\n",
    "student.eat()  # 继承父类的eat方法\n",
    "student.work()  # 调用重写的work方法\n",
    "student.study()  # 调用子类特有方法"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 151,
   "id": "908cd5ec",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "李老师在吃饭（父类默认实现）\n",
      "李老师在讲课（老师的工作）\n"
     ]
    }
   ],
   "source": [
    "# 实例化Teacher子类\n",
    "teacher = Teacher(\"李老师\")\n",
    "teacher.eat()  # 继承父类的eat方法\n",
    "teacher.work()  # 调用重写的work方法"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "bf7e1640",
   "metadata": {},
   "source": [
    "普通继承说明：\n",
    "\n",
    "父类 Person 是普通类，可直接实例化，方法有具体实现。\n",
    "\n",
    "子类通过 class 子类名(父类名) 继承，可重写父类方法（如 work），也可继承父类方法（如 eat）。\n",
    "\n",
    "子类可新增自己的方法（如 Student.study）。\n",
    "\n",
    "缺点：无法强制子类必须重写某个方法（比如如果 Student 忘了实现 work，也不会报错，可能导致逻辑漏洞）。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9c785d4f",
   "metadata": {},
   "source": [
    "# 装饰器@override"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "dc8686b2",
   "metadata": {},
   "source": [
    "@override 是 Python 3.12+ 新增的装饰器（低版本需从 typing_extensions 导入），用于明确标记子类重写了父类的方法，作用是：\n",
    "\n",
    "增强代码可读性：一眼看出该方法是重写父类的。\n",
    "\n",
    "自动检查：如果父类中没有对应的方法，会报错（避免拼写错误或误写方法名）。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 152,
   "id": "4217efbc",
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing_extensions import override  # 低版本Python需导入\n",
    "\n",
    "# 1. 父类（普通类）\n",
    "class Person:\n",
    "    def __init__(self, name):\n",
    "        self.name = name\n",
    "\n",
    "    def eat(self):\n",
    "        print(f\"{self.name}在吃饭\")\n",
    "\n",
    "    def work(self):\n",
    "        print(f\"{self.name}在工作\")\n",
    "\n",
    "# 2. 子类Student（使用@override）\n",
    "class Student(Person):\n",
    "    # 用@override标记：明确重写父类的work方法\n",
    "\n",
    "    def work(self):\n",
    "        print(f\"{self.name}在写作业（学生的工作）\")\n",
    "\n",
    "    # 错误示例：如果父类没有play方法，@override会报错\n",
    "\n",
    "    def study(self):\n",
    "        print(f\"{self.name}在学习\")\n",
    "\n",
    "# 3. 子类Teacher（使用@override）\n",
    "class Teacher(Person):\n",
    "    # 用@override标记：重写父类的eat方法\n",
    "\n",
    "    def eat(self):\n",
    "        print(f\"{self.name}快速吃饭（赶时间上课）\")\n",
    "\n",
    "    # 重写work方法，未加@override（允许，但不推荐）\n",
    "    def work(self):\n",
    "        print(f\"{self.name}在讲课（老师的工作）\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 153,
   "id": "2ce9beaa",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "小红在写作业（学生的工作）\n"
     ]
    }
   ],
   "source": [
    "student = Student(\"小红\")\n",
    "student.work()  # 调用重写的work（带override）"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 154,
   "id": "8a0dfabb",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "错误详情：'Student' object has no attribute 'play'\n",
      " traceback:Traceback (most recent call last):\n",
      "  File \"/tmp/ipykernel_103737/231382040.py\", line 7, in <module>\n",
      "    student.play()\n",
      "AttributeError: 'Student' object has no attribute 'play'\n",
      "\n"
     ]
    }
   ],
   "source": [
    "import traceback\n",
    "try:\n",
    "    # 尝试定义Student类时，@override装饰play方法会直接报错\n",
    "    # （因为类定义阶段就会检查override是否有效）\n",
    "    student = Student(\"小红\")\n",
    "    # AttributeError: 'Student' object has no attribute 'play'\n",
    "    student.play()\n",
    "except Exception as e:\n",
    "    print(f\"错误详情：{e}\\n traceback:{traceback.format_exc()}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 155,
   "id": "7db3b22d",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "王老师快速吃饭（赶时间上课）\n",
      "王老师在讲课（老师的工作）\n"
     ]
    }
   ],
   "source": [
    "teacher = Teacher(\"王老师\")\n",
    "teacher.eat()  # 调用重写的eat（带override）\n",
    "teacher.work()  # 调用重写的work（不带override）"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "83645cf3",
   "metadata": {},
   "source": [
    "# 抽象方法(强制子类实现)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "dd557ea5",
   "metadata": {},
   "source": [
    "当父类 Person 的所有方法都是抽象方法时，它就成为一个 “纯接口类”\n",
    "\n",
    "自身不能实例化，\n",
    "\n",
    "且强制所有子类必须实现所有抽象方法，否则子类也无法实例化。\n",
    "\n",
    "**不能靠约定，靠嘴说，而是靠代码约束。**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 156,
   "id": "33bf694a",
   "metadata": {},
   "outputs": [],
   "source": [
    "from abc import ABC, abstractmethod  # 必须导入ABC和abstractmethod\n",
    "# 1. 抽象基类（全抽象方法）\n",
    "class Person(ABC):  # 继承ABC，标记为抽象类\n",
    "    def __init__(self, name):\n",
    "        self.name = name  # 抽象类可以有构造方法（初始化属性）\n",
    "\n",
    "    # 抽象方法：无实现，强制子类必须实现\n",
    "    @abstractmethod\n",
    "    def eat(self):\n",
    "        \"\"\"定义吃饭的接口，子类必须实现具体逻辑\"\"\"\n",
    "        pass\n",
    "\n",
    "    # 抽象方法：带参数的接口\n",
    "    @abstractmethod\n",
    "    def work(self):\n",
    "        \"\"\"定义工作的接口\n",
    "        \"\"\"\n",
    "        pass"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 157,
   "id": "1b6e6fd6",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "实例化抽象类报错：Can't instantiate abstract class Person with abstract methods eat, work\n",
      "traceback:Traceback (most recent call last):\n",
      "  File \"/tmp/ipykernel_103737/2626580753.py\", line 4, in <module>\n",
      "    person = Person(\"抽象人\")\n",
      "TypeError: Can't instantiate abstract class Person with abstract methods eat, work\n",
      "\n"
     ]
    }
   ],
   "source": [
    "# 错误：抽象基类不能实例化\n",
    "import traceback\n",
    "try:\n",
    "    person = Person(\"抽象人\")\n",
    "except TypeError as e:\n",
    "    print(f\"实例化抽象类报错：{e}\\ntraceback:{traceback.format_exc()}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 158,
   "id": "6d34d093",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "小刚在学校食堂吃饭\n",
      "小刚在写作业（学生的工作）\n"
     ]
    }
   ],
   "source": [
    "# 2. 子类Student（必须实现所有抽象方法）\n",
    "class Student(Person):\n",
    "    def eat(self):\n",
    "        print(f\"{self.name}在学校食堂吃饭\")\n",
    "    def work(self):\n",
    "        print(f\"{self.name}在写作业（学生的工作）\")\n",
    "\n",
    "student = Student(\"小刚\")\n",
    "student.eat()\n",
    "student.work()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 159,
   "id": "e26cf771",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "张老师在教师餐厅吃饭\n",
      "张老师在讲课（老师的工作）\n"
     ]
    }
   ],
   "source": [
    "# 3. 子类Teacher（必须实现所有抽象方法）\n",
    "class Teacher(Person):\n",
    "    def eat(self):\n",
    "        print(f\"{self.name}在教师餐厅吃饭\")\n",
    "    def work(self):\n",
    "        print(f\"{self.name}在讲课（老师的工作）\")\n",
    "teacher = Teacher(\"张老师\")\n",
    "teacher.eat()\n",
    "teacher.work()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "bb141e1c",
   "metadata": {},
   "source": [
    "抽象方法定义接口更多示例"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 160,
   "id": "eb32b5ac",
   "metadata": {},
   "outputs": [],
   "source": [
    "from abc import ABC, abstractmethod\n",
    "# 1. 抽象基类（抽象方法带参数）\n",
    "class Person(ABC):\n",
    "    def __init__(self, name):\n",
    "        self.name = name  # 抽象类可定义属性\n",
    "\n",
    "    # 抽象方法：带参数（meal: 食物名称）\n",
    "    @abstractmethod\n",
    "    def eat(self, meal: str):\n",
    "        \"\"\"吃指定的食物\n",
    "        :param meal: 食物名称（如\"米饭\"、\"面条\"）\n",
    "        \"\"\"\n",
    "        pass\n",
    "\n",
    "    # 抽象方法：带多个参数（hours: 时长，content: 工作内容）\n",
    "    @abstractmethod\n",
    "    def work(self, hours: int, content: str):\n",
    "        \"\"\"进行指定内容的工作\n",
    "        :param hours: 工作时长（小时）\n",
    "        :param content: 工作具体内容\n",
    "        \"\"\"\n",
    "        pass\n",
    "\n",
    "    # 抽象方法：带返回值\n",
    "    @abstractmethod\n",
    "    def get_role(self) -> str:\n",
    "        \"\"\"返回角色名称\"\"\"\n",
    "        pass"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 161,
   "id": "53fb308f",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "=== 场景1：子类漏实现抽象方法 ===\n",
      "错误类型：TypeError\n",
      "错误详情：Can't instantiate abstract class BadStudent1 with abstract method get_role\n",
      "traceback:Traceback (most recent call last):\n",
      "  File \"/tmp/ipykernel_103737/941218995.py\", line 10, in <module>\n",
      "    bad_stu1 = BadStudent1(\"不及格学生1\")\n",
      "TypeError: Can't instantiate abstract class BadStudent1 with abstract method get_role\n",
      "\n"
     ]
    }
   ],
   "source": [
    "# 2. 错误子类1：漏实现抽象方法（未实现get_role）\n",
    "class BadStudent1(Person):\n",
    "    def eat(self, meal: str):\n",
    "        print(f\"{self.name}在吃{meal}（学生餐）\")\n",
    "    def work(self, hours: int, content: str):\n",
    "        print(f\"{self.name}花了{hours}小时做{content}（作业）\")\n",
    "# 场景1：子类漏实现抽象方法\n",
    "print(\"=== 场景1：子类漏实现抽象方法 ===\")\n",
    "try:\n",
    "    bad_stu1 = BadStudent1(\"不及格学生1\")\n",
    "except Exception as e:\n",
    "    print(f\"错误类型：{type(e).__name__}\\n错误详情：{e}\\ntraceback:{traceback.format_exc()}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 162,
   "id": "a6d8c062",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 3. 错误子类2：参数不匹配（work少一个参数）\n",
    "class BadStudent2(Person):\n",
    "    def eat(self, meal: str):\n",
    "        print(f\"{self.name}吃{meal}\")\n",
    "    # 错误：work方法少了content参数（与父类抽象方法签名不匹配）\n",
    "    def work(self, hours: int):\n",
    "        print(f\"{self.name}学习了{hours}小时\")\n",
    "    def get_role(self) -> str:\n",
    "        return \"学生\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 163,
   "id": "ecdabaa5",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "=== 场景2-1：方法参数不匹配 ===\n"
     ]
    }
   ],
   "source": [
    "# 场景2-1：子类方法参数与父类不匹配\n",
    "print(\"=== 场景2-1：方法参数不匹配 ===\")\n",
    "try:\n",
    "    bad_stu2_1 = BadStudent2(\"不及格学生2\")\n",
    "except Exception as e:\n",
    "    print(f\"错误类型：{type(e).__name__}\\n错误详情：{e}\\ntraceback:{traceback.format_exc()}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 164,
   "id": "9a3922e2",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "=== 场景2-2：方法参数不匹配 ===\n",
      "不及格学生2学习了2小时\n"
     ]
    }
   ],
   "source": [
    "# 场景2-2：子类方法参数与父类不匹配\n",
    "print(\"=== 场景2-2：方法参数不匹配 ===\")\n",
    "try:\n",
    "    bad_stu2_2 = BadStudent2(\"不及格学生2\")\n",
    "    bad_stu2_2.work(2)\n",
    "except Exception as e:\n",
    "    print(f\"错误类型：{type(e).__name__}\\n错误详情：{e}\\ntraceback:{traceback.format_exc()}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 165,
   "id": "75b14810",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "=== 场景3：正确实现的Student ===\n",
      "小明在学校吃汉堡\n",
      "小明用2小时完成数学作业（学生作业）\n",
      "角色：学生\n",
      "\n"
     ]
    }
   ],
   "source": [
    "# 4. 正确子类：Student（完整实现所有抽象方法，参数匹配）\n",
    "class Student(Person):\n",
    "\n",
    "    def eat(self, meal: str):  # 匹配父类参数meal: str\n",
    "        print(f\"{self.name}在学校吃{meal}\")\n",
    "        \n",
    "    def work(self, hours: int, content: str):  # 匹配两个参数\n",
    "        print(f\"{self.name}用{hours}小时完成{content}（学生作业）\")\n",
    "        \n",
    "    def get_role(self) -> str:\n",
    "        return \"学生\"\n",
    "# 场景3：正确子类Student\n",
    "print(\"=== 场景3：正确实现的Student ===\")\n",
    "try:\n",
    "    stu = Student(\"小明\")\n",
    "    stu.eat(\"汉堡\")  # 传入参数meal\n",
    "    stu.work(2, \"数学作业\")  # 传入hours和content\n",
    "    print(f\"角色：{stu.get_role()}\\n\")\n",
    "except Exception as e:\n",
    "    print(f\"错误：{e}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 166,
   "id": "cd692580",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "=== 场景4：正确实现的Teacher ===\n",
      "李老师在办公室吃三明治\n",
      "李老师用4小时准备语文课课件（教学内容）\n",
      "角色：教师\n"
     ]
    }
   ],
   "source": [
    "# 5. 正确子类：Teacher\n",
    "class Teacher(Person):\n",
    "\n",
    "    def eat(self, meal: str):\n",
    "        print(f\"{self.name}在办公室吃{meal}\")\n",
    "\n",
    "\n",
    "    def work(self, hours: int, content: str):\n",
    "        print(f\"{self.name}用{hours}小时准备{content}（教学内容）\")\n",
    "\n",
    "\n",
    "    def get_role(self) -> str:\n",
    "        return \"教师\"\n",
    "# 正确子类Teacher\n",
    "print(\"=== 场景4：正确实现的Teacher ===\")\n",
    "try:\n",
    "    tea = Teacher(\"李老师\")\n",
    "    tea.eat(\"三明治\")\n",
    "    tea.work(4, \"语文课课件\")\n",
    "    print(f\"角色：{tea.get_role()}\")\n",
    "except Exception as e:\n",
    "    print(f\"错误：{e}\")"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "langchain-course (3.10.12)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.12"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
